Skip to content

feat: Add Cloud Backup and Restore via GitHub Gists#3826

Merged
Martí Climent (marticliment) merged 27 commits into
Devolutions:mainfrom
theguy000:feat/github-gist-backup
Jul 9, 2025
Merged

feat: Add Cloud Backup and Restore via GitHub Gists#3826
Martí Climent (marticliment) merged 27 commits into
Devolutions:mainfrom
theguy000:feat/github-gist-backup

Conversation

@theguy000
Copy link
Copy Markdown
Contributor

@theguy000 TheGuy (theguy000) commented Jul 8, 2025

TODO

  • Allow dynamic fetching of appropiate gist (no need to store id)
  • Allow uploading different backups for different machines
  • Allow retrieving available backups
  • Allow selection and download of a backup gist
  • Integrate Cloud backup into automated backup system

This pull request introduces a new feature allowing users to back up and restore their UniGetUI settings and installed packages to the cloud using private GitHub Gists.

What's New:

  • GitHub Authentication: Users can now log in with their GitHub account via a secure OAuth2 flow.
  • Cloud Backup: Once authenticated, users can back up their application settings and a list of their installed packages to a private GitHub Gist. The system will create a new private Gist named "UniGetUI Backup" or
    update an existing one.
  • Cloud Restore: Authenticated users can restore their settings and packages from the Gist.
    • Restoring settings will overwrite current local settings.
    • Restoring packages will load the package list into the "Package Bundles" page for review and installation.
  • Secure Token Handling: The user's GitHub access token is stored securely in the Windows Credential Manager (PasswordVault) and is not accessible as plain text.

Key Technical Changes:

  • GitHubAuthService: A new service to handle the entire GitHub OAuth2 authentication process, using a localhost loopback redirect to receive the callback.
  • GitHubBackupService: A new service responsible for creating, retrieving, and updating the backup Gist.
  • SecureTokenManager: A new helper class to securely store and retrieve the GitHub token from the PasswordVault.
  • UI Enhancements: The Settings -> Backup and Restore page has been updated with a new "Cloud Backup and Restore" section to manage authentication and trigger backup/restore operations.
  • Secrets Management: The build script (apply_versions.py) now generates a Secrets.cs file from a GHA_SECRET environment variable to avoid hardcoding the GitHub Client Secret. Secrets.cs is included in .gitignore.
  • I have read the contributing guidelines, and I agree with the Code of Conduct.
  • Have you checked that there aren't other open pull requests for the same changes?
  • Have you tested that the committed code can be executed without errors?
  • This PR is not composed of garbage changes used to farm GitHub activity to enter potential Crypto AirDrops.
    Any user suspected of farming GitHub activity with crypto purposes will get banned. Submitting broken code wastes the contributors' time, who have to spend their free time reviewing, fixing, and testing code that does not even compile breaks other features, or does not introduce any useful changes. I appreciate your understanding.


Closes #XXXX

Relates to #3726

google-labs-jules Bot and others added 8 commits July 7, 2025 15:39
- Added IdentityModel.OidcClient and Octokit packages.
- Implemented SecureTokenManager for secure GitHub token storage using PasswordVault.
- Created GitHubAuthService to handle OIDC authentication flow with GitHub, including a WinUIBrowser for system browser interaction.
- Configured protocol activation for 'unigetui-auth://' in App.xaml.cs and Package.appxmanifest to handle OAuth callback.
- Added initial structure for GistService to create Gists using Octokit.

Note: UI integration and explicit testing steps were to be skipped as per request.
Adds functionality to allow users to:
- Log in with their GitHub account using OAuth.
- Backup their UniGetUI settings to a private GitHub Gist.
- Restore their UniGetUI settings from a GitHub Gist.

Key changes include:
- New services: GitHubAuthService for handling GitHub OAuth and
  GitHubBackupService for Gist operations.
- UI additions to Internet settings for GitHub login/logout.
- UI additions to Backup settings for Gist backup/restore buttons.
- Enhancements to SettingsEngine for string-based import/export.
- New settings keys for GitHub username and Gist ID.
This commit introduces a new feature allowing users to back up and restore their UniGetUI settings and installed packages to a private GitHub Gist.

Key changes:

- **GitHub Authentication:**
    - Implements an OAuth2 flow to securely connect to the user's GitHub account.
    - Uses a local loopback server for the OAuth redirect, improving the user experience.
    - Securely stores the OAuth token and user information.

- **Backup and Restore Logic:**
    - Creates a private GitHub Gist to store backup files.
    - Backs up both settings (`unigetui.settings.json`) and a list of installed packages (`unigetui.packages.ubundle`).
    - Allows restoring settings and packages separately. Restoring packages loads them into the "Package Bundles" page for installation.

- **UI Enhancements:**
    - The "Backup" settings page is redesigned as "Backup and Restore".
    - Provides clear login/logout functionality and status indicators.
    - Adds separate buttons for backing up, restoring settings, and restoring packages.

- **Build Process:**
    - The build script now generates a `Secrets.cs` file for the GitHub OAuth client details from environment variables, which is git-ignored.
 it includes updates to several source files in the UniGetUI project, such as SecureTokenManager.cs for security settings, SettingsEngine_ImportExport.cs for settings management, and Internet.xaml along with its code-behind file Internet.xaml.cs for UI adjustments related to internet settings. These changes ensure the codebase remains consistent and up-to-date after the removal of the documentation file.
@marticliment
Copy link
Copy Markdown
Collaborator

Thanks!
I will review and merge in a few days.

@marticliment
Copy link
Copy Markdown
Collaborator

Hey TheGuy (@theguy000),

I've been looking at the implementation, and overall I really like it, thanks!
However, there are 2 things I'd like to discuss:

  1. I'll need to add multi-device support. If a user logs in into UniGetUI from two different machines, logging in with the same GH account, and enables backup a conflict will occurr. The same goes when restoring packages, the user should be able to pick which backup to restore.
  2. For the moment, I will disable settings backup/exporting. I am looking into ways to integrating settings into .ubundle files, and I also am afraid of user confusion between the different options.

For both things, I can take care of them, and once I have I will merge the PR and publish a beta release so the features can be tested out.

@mrixner
Copy link
Copy Markdown
Contributor

mrixner commented Jul 8, 2025

Would you be interested in having a "merge settings" option in addition to an "overwrite local settings" option? If both are willing, I'm willing to open a PR to this branch for the addition of settings merges as well (since unset settings don't exist).

…its identifier locally. Multiple user support added
@marticliment
Copy link
Copy Markdown
Collaborator

Would you be interested in having a "merge settings" option in addition to an "overwrite local settings" option? If both are willing, I'm willing to open a PR to this branch for the addition of settings merges as well (since unset settings don't exist).

You mean vscode-like settings sync?

@theguy000
Copy link
Copy Markdown
Contributor Author

I mean not a bad feature to have.

@mrixner
Copy link
Copy Markdown
Contributor

mrixner commented Jul 8, 2025

Probably? My VS Code hasn't synced in almost two years so I don't really know... But, like, settings from the server wouldn't (or would, depending on a setting / dialog button) overwrite locally set settings, but if they're not there locally the server setting would apply.

I guess this isn't really a common use case, but it might help with (as previously mentioned) multi-device support - updates would be merged as well, so the latest configuration is always synced to all devices.

@marticliment
Copy link
Copy Markdown
Collaborator

To be honest, I thought about settings sync, but then I thougt it would be expected for different devices to have different settings (for example, I don't have vcpkg or npm on my laptop, but I do on my desktop), so I guess it kinda makes sense to not sync settings across devices.

Perhaps they can also be backed up to GitHub like with the bundles (like TheGuy (@theguy000)'s original implementation), but I don't see an immediate benefit (again, you'd be importing the settings on a new machine (or same machine with an erased OS, otherwhise settings wouldn't have been erased), with different software installed, etc.)

@theguy000
Copy link
Copy Markdown
Contributor Author

TheGuy (theguy000) commented Jul 8, 2025

How about this -

  1. Settings Backup: Already device-specific.
  2. Packages Restore: Needs to show all backups from all devices, with options to filter and merge.
  3. Remove Device: Add functionality to delete backups for a specific machine.
  4. Merge All: Combine package lists from multiple selected devices.

@mrixner
Copy link
Copy Markdown
Contributor

mrixner commented Jul 9, 2025

To be honest, I thought about settings sync, but then I thougt it would be expected for different devices to have different settings (for example, I don't have vcpkg or npm on my laptop, but I do on my desktop), so I guess it kinda makes sense to not sync settings across devices.

Perhaps they can also be backed up to GitHub like with the bundles (like TheGuy (@theguy000)'s original implementation), but I don't see an immediate benefit (again, you'd be importing the settings on a new machine (or same machine with an erased OS, otherwhise settings wouldn't have been erased), with different software installed, etc.)

Yeah, I think you're probably right.

@marticliment
Copy link
Copy Markdown
Collaborator

Martí Climent (marticliment) commented Jul 9, 2025

1 and 2 are already intrinsic on the multi-device support, and 4, it is very easily achievable by loading different backups and installing the wanted packages.

3, While intrastructure-wise it is very easy to implement (even more with the multi-file support already done), I still think it will not be useful, and will confuse users (I have received complaints of nonworking bundles because people were confusing bundles with import/export settings, and they are located on different parts of the UI, I don't think it is worth the hassle)

@marticliment Martí Climent (marticliment) linked an issue Jul 9, 2025 that may be closed by this pull request
2 tasks
@marticliment
Copy link
Copy Markdown
Collaborator

This is how the UI will be:

image
image
image
image

@marticliment Martí Climent (marticliment) merged commit c0b7013 into Devolutions:main Jul 9, 2025
2 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds cloud backup and restore functionality using private GitHub Gists, alongside enhanced local backup support and automated secret generation.

  • Introduces GitHubAuthService and GitHubBackupService for OAuth2 login and gist-based backups
  • Updates settings UI (Backup.xaml) to handle separate cloud and local backup flows
  • Implements build‐time secrets file generation via PowerShell and project file targets

Reviewed Changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/UniGetUI/UniGetUI.csproj Added target to generate Secrets.Generated.cs
src/UniGetUI/Services/generate-secrets.ps1 Powershell script to emit client ID/secret code files
src/UniGetUI/Services/GitHubBackupService.cs New service to create, update, and list GitHub Gist backups
src/UniGetUI/Services/GitHubAuthService.cs New OAuth2 flow with loopback listener and token storage
src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml Extended UI for cloud/local backups
src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs Logic for cloud and local backup button handlers
src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs Split backup logic into BackupPackages_CLOUD and _LOCAL
src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs Refactored open/save bundle methods to string-based APIs
Comments suppressed due to low confidence (4)

src/UniGetUI/Services/UserAvatar.cs:165

  • [nitpick] Local variable GHClient should use camelCase (e.g., ghClient or gitHubClient) to align with C# naming conventions.
            var GHClient = authClient.CreateGitHubClient();

src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs:256

  • New cloud backup methods (BackupPackages_CLOUD, BackupPackages_LOCAL) lack corresponding unit tests. Consider adding tests to cover these scenarios.
            var packagesContent = await InstalledPackagesPage.GenerateBackupContents();

src/UniGetUI/Services/GitHubBackupService.cs:103

  • The fallback empty array [] is not valid C# syntax. Consider using Enumerable.Empty<string>() or new string[0] for an empty fallback.
                .Select(f => $"{f.Key.Split(' ')[^1]} ({CoreTools.FormatAsSize(f.Value.Size)})") ?? [];

src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs:187

  • [nitpick] Fetching all user gists on each call can be slow for users with many gists. Consider caching the gist list or limiting the request scope.
                );

Comment thread src/UniGetUI/Services/GitHubBackupService.cs
Comment thread src/UniGetUI/Services/GitHubBackupService.cs
Comment thread src/UniGetUI/Services/generate-secrets.ps1
@theguy000
Copy link
Copy Markdown
Contributor Author

When will the new version be released?

@marticliment
Copy link
Copy Markdown
Collaborator

next beta in a few days, stable will depend on how many issues are found, but expect at least a week from the beta release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE REQUEST] Add backup option by logging in a user.

4 participants